////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
//  File name:   visareas.h
//  Version:     v1.00
//  Created:     18/12/2002 by Vladimir Kajalin
//  Compilers:   Visual Studio.NET
//  Description: visibility areas header
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#ifndef VisArea_H
#define VisArea_H

#include "BasicArea.h"
#include "CRAMSolution.h"
#include "CullBuffer.h"





	#define bool_type bool


// Unique identifier for each VisArea instance
typedef unsigned int VisAreaId;

typedef uint64 VisAreaGUID;

#define ReservedVisAreaBytes 384

enum EVisAreaColdDataType
{
	eCDT_Generic = 0,
	eCDT_Portal
};

struct SGenericColdData
{
	ILINE void ResetGenericData()
	{
		m_dataType = eCDT_Generic;
	}

	EVisAreaColdDataType m_dataType;
	char m_sName[64];
};

struct SPortalColdData : public SGenericColdData
{
	ILINE void ResetPortalData()
	{
		m_dataType = eCDT_Portal;
	}

	struct CRNTmpData * m_pRNTmpData;
};

struct CVisArea : public IVisArea, public CBasicArea
{
	static void StaticReset();

	// editor interface
	virtual void Update(const Vec3 * pPoints, int nCount, const char * szName, const SVisAreaInfo & info);

	CVisArea();
	CVisArea(VisAreaGUID visGUID);
	virtual ~CVisArea();

	bool operator ==(const CVisArea& v1) const { return &v1 == this; }
	
	void Init();
	ILINE SGenericColdData * GetColdData() { return m_pVisAreaColdData; }
	ILINE void SetColdDataPtr(SGenericColdData * pColdData) { m_pVisAreaColdData = pColdData; }
	bool IsSphereInsideVisArea(const Vec3 & vPos, const f32 fRadius);
	bool IsPointInsideVisArea(const Vec3 & vPos);
  bool IsBoxOverlapVisArea(const AABB & objBox);
	bool ClipToVisArea(bool bInside, Sphere& sphere, Vec3 const& vNormal);
	bool FindVisArea(IVisArea * pAnotherArea, int nMaxRecursion, bool bSkipDisabledPortals);
	bool FindVisAreaReqursive(IVisArea * pAnotherArea, int nMaxReqursion, bool bSkipDisabledPortals, PodArray<CVisArea*> & arrVisitedParents);
  bool GetDistanceThruVisAreas(AABB vCurBoxIn, IVisArea * pTargetArea, const AABB & targetBox, int nMaxReqursion, float & fResDist);
  bool GetDistanceThruVisAreasReq(AABB vCurBoxIn, float fCurDistIn, IVisArea * pTargetArea, const AABB & targetBox, int nMaxReqursion, float & fResDist, CVisArea * pPrevArea, int nCallID);
	void FindSurroundingVisArea( int nMaxReqursion, bool bSkipDisabledPortals, PodArray<IVisArea*> * pVisitedAreas = NULL, int nMaxVisitedAreas = 0, int nDeepness = 0);
	void FindSurroundingVisAreaReqursive( int nMaxReqursion, bool bSkipDisabledPortals, PodArray<IVisArea*> * pVisitedAreas, int nMaxVisitedAreas, int nDeepness, PodArray<CVisArea*> * pUnavailableAreas );
	int GetVisFrameId();
	Vec3 GetConnectionNormal(CVisArea * pPortal);
	void PreRender(int nReqursionLevel, CCamera CurCamera, CVisArea * pParent, CVisArea * pCurPortal, bool_type * pbOutdoorVisible, PodArray<CCamera> * plstOutPortCameras, bool_type * pbSkyVisible, bool_type * pbOceanVisible, PodArray<CVisArea*> & lstVisibleAreas, int nEngineFrameID);
	void UpdatePortalCameraPlanes(CCamera & cam, Vec3 * pVerts, bool bMergeFrustums);
	int GetVisAreaConnections(IVisArea ** pAreas, int nMaxConnNum, bool bSkipDisabledPortals = false);
  int GetRealConnections(IVisArea ** pAreas, int nMaxConnNum, bool bSkipDisabledPortals = false);
	bool IsPortalValid();
	bool IsPortalIntersectAreaInValidWay(CVisArea * pPortal);
	bool IsPortal();
	bool IsShapeClockwise();
	bool IsAffectedByOutLights() { return m_bAffectedByOutLights; }
	bool IsActive() { return m_bActive || (GetCVars()->e_Portals==4); }
	void UpdateGeometryBBox();
  void DrawAreaBoundsIntoCBuffer(class CCullBuffer * pCBuffer);
  void ClipPortalVerticesByCameraFrustum(PodArray<Vec3> * pPolygon, const CCamera & cam);
  void GetMemoryUsage(ICrySizer*pSizer);
	bool IsConnectedToOutdoor();
	bool IsIgnoringGI() const { return m_bIgnoreGI; }	
	const char * GetName() { return m_pVisAreaColdData->m_sName; }
	int SaveHeader(byte * & pData, int & nDataSize);
	int SaveObjetsTree(byte * & pData, int & nDataSize, std::vector<IStatObj*> * pStatObjTable, std::vector<IMaterial*> * pMatTable, std::vector<IStatInstGroup*> * pStatInstGroupTable, EEndian eEndian, SHotUpdateInfo * pExportInfo, byte * pHead, const Vec3 &segmentOffset);
	int GetData(byte * & pData, int & nDataSize, std::vector<IStatObj*> * pStatObjTable, std::vector<IMaterial*> * pMatTable, std::vector<IStatInstGroup*> * pStatInstGroupTable, EEndian eEndian, SHotUpdateInfo * pExportInfo);
	int GetSegmentData(byte * & pData, int & nDataSize, std::vector<IStatObj*> * pStatObjTable, std::vector<IMaterial*> * pMatTable, std::vector<IStatInstGroup*> * pStatInstGroupTable, EEndian eEndian, SHotUpdateInfo * pExportInfo, const Vec3 &segmentOffset);
  template <class T>
  int LoadHeader_T(T * & f, int & nDataSizeLeft, EEndian eEndian, int &objBlockSize);
  template <class T>
  int LoadObjectsTree_T(T * & f, int & nDataSizeLeft, std::vector<IStatObj*> * pStatObjTable, std::vector<IMaterial*> * pMatTable, EEndian eEndian, SHotUpdateInfo * pExportInfo, const int objBlockSize, const Vec3 &segmentOffset);
  template <class T>
  int Load_T(T * & f, int & nDataSize, std::vector<IStatObj*> * pStatObjTable, std::vector<IMaterial*> * pMatTable, EEndian eEndian, SHotUpdateInfo * pExportInfo);
  int Load(byte * & f, int & nDataSizeLeft, std::vector<IStatObj*> * pStatObjTable, std::vector<IMaterial*> * pMatTable, EEndian eEndian, SHotUpdateInfo * pExportInfo);
  int Load(FILE * & f, int & nDataSizeLeft, std::vector<IStatObj*> * pStatObjTable, std::vector<IMaterial*> * pMatTable, EEndian eEndian, SHotUpdateInfo * pExportInfo);
	const AABB* GetAABBox() const;
	const AABB* GetStaticObjectAABBox() const;
  void UpdateOcclusionFlagInTerrain();
  void AddConnectedAreas(PodArray<CVisArea *> & lstAreas, int nMaxRecursion);
	void GetShapePoints(const Vec3 *&pPoints, size_t &nPoints);
	float GetHeight();
	float CalcSignedArea();

	void OffsetPosition(const Vec3& delta);
	static VisAreaGUID GetGUIDFromFile(byte *f, EEndian eEndian);
	VisAreaGUID GetGUID() const { return m_nVisGUID; }

	//RAM
	void		CalcAmbientCube(f32* pAmbientCube,const Vec3& rAABBMin,const Vec3& rAABBMax);
	void		CalcHQAmbientCube(f32* pAmbientCube,const Vec3& rAABBMin,const Vec3& rAABBMax);
	void		RAMAssign(CDLight* pLight);
	void		RAMRelease(CDLight* pLight);

  const Vec3 GetFinalAmbientColor();

	static PodArray<CVisArea*> m_lUnavailableAreas;
	static PodArray<CVisArea*> m_lVisitedParents;
	static PodArray<Vec3> s_tmpLstPortVertsClipped;
	static PodArray<Vec3> s_tmpLstPortVertsSS;
	static PodArray<Vec3> s_tmpPolygonA;
	static PodArray<IRenderNode*>	s_tmpLstLights;
	static PodArray<CTerrainNode*> s_tmpLstTerrainNodeResult;
	static CPolygonClipContext s_tmpClipContext;
	static int s_nGetDistanceThruVisAreasCallCounter;

	VisAreaGUID m_nVisGUID;
	PodArray<CVisArea*> m_lstConnections;
	Vec3 m_vConnNormals[2];
	int m_nRndFrameId;
	float m_fGetDistanceThruVisAreasMinDistance;
	int m_nGetDistanceThruVisAreasLastCallID;

#ifdef SUPPORT_LM
	CRAMSolution	m_RAMSolution[2];	//double buffered
	int m_RAMFrame;
#endif // SUPPORT_LM

	PodArray<Vec3> m_lstShapePoints;
	float m_fHeight;
	Vec3 m_vAmbientColor;
	float m_fDistance;
	float m_fViewDistRatio;
	CCamera * m_arrOcclCamera[MAX_RECURSION_LEVELS];
	PodArray<CCamera> m_lstCurCameras;
	SGenericColdData * m_pVisAreaColdData;
	bool_type m_bAffectedByOutLights;
	bool_type m_bSkyOnly;
	bool_type m_bOceanVisible;
	bool_type m_bDoubleSide;
	bool_type m_bUseDeepness;
	bool_type m_bUseInIndoors;
	bool_type m_bThisIsPortal;
	bool_type m_bIgnoreSky;
	bool_type m_bActive;
	bool_type m_bIgnoreGI;
};

struct CSWVisArea : public CVisArea, public _i_reference_target_t
{
	CSWVisArea() : CVisArea(), m_nSlotID(-1) {}
	~CSWVisArea() {}
	
	void Release()
	{
		--m_nRefCounter;
		if(m_nRefCounter < 0)
		{
			assert(0);
			CryFatalError( "Deleting Reference Counted Object Twice" );
		}
	}

	int Load(byte * & f, int & nDataSizeLeft, std::vector<IStatObj*> * pStatObjTable, std::vector<IMaterial*> * pMatTable, EEndian eEndian, SHotUpdateInfo * pExportInfo, const Vec3 &segmentOffset, const Vec2 &indexOffset);

	int m_nSlotID;
};

struct SAABBTreeNode
{
	SAABBTreeNode( PodArray<CVisArea*> & lstAreas, AABB box, int nMaxRecursion = 0);
	~SAABBTreeNode( ) ;
	CVisArea * FindVisarea(const Vec3 & vPos);
	SAABBTreeNode* GetTopNode(const AABB& box, void** pNodeCache);
	bool IntersectsVisAreas(const AABB& box);
	int ClipOutsideVisAreas(Sphere& sphere, Vec3 const& vNormal);
	void OffsetPosition(const Vec3 &delta);

	AABB nodeBox;
	PodArray<CVisArea*> nodeAreas;
	SAABBTreeNode * arrChilds[2];
};

struct TTopologicalSorter;

struct CVisAreaSegmentData
{
	// active vis areas in current segment
	std::vector<int> m_visAreaIndices;
};

struct CVisAreaManager : public IVisAreaManager, Cry3DEngineBase
{
	CVisArea * m_pCurArea, * m_pCurPortal;
	PodArray<CVisArea * > m_lstActiveEntransePortals;

	PodArray<CVisArea*> m_lstVisAreas;
  PodArray<CVisArea*> m_lstPortals;
  PodArray<CVisArea*> m_lstOcclAreas;
	PodArray<CVisArea*> m_segVisAreas;
	PodArray<CVisArea*> m_segPortals;
	PodArray<CVisArea*> m_segOcclAreas;
	PodArray<CVisArea*> m_lstActiveOcclVolumes;
	PodArray<CVisArea*> m_lstIndoorActiveOcclVolumes;
	PodArray<CVisArea*> m_lstVisibleAreas;
	PodArray<CVisArea*> m_tmpLstUnavailableAreas;
	PodArray<CVisArea*> m_tmpLstLightBoxAreas;
  bool_type m_bOutdoorVisible;
  bool_type m_bSkyVisible;
  bool_type m_bOceanVisible;
	bool_type m_bSunIsNeeded;
  PodArray<CCamera> m_lstOutdoorPortalCameras;
	PodArray<IVisAreaCallback*> m_lstCallbacks;
	SAABBTreeNode * m_pAABBTree;

	CVisAreaManager();
	~CVisAreaManager();
	void UpdateAABBTree();
	void SetCurAreas();
	PodArray<CVisArea*> * GetActiveEntransePortals() { return &m_lstActiveEntransePortals; }
	void PortalsDrawDebug();
	bool IsEntityVisible(IRenderNode * pEnt);
	bool IsOutdoorAreasVisible();
  bool IsSkyVisible();
  bool IsOceanVisible();
	CVisArea * CreateVisArea(VisAreaGUID visGUID);
	bool DeleteVisArea(CVisArea * pVisArea);
	bool SetEntityArea(IRenderNode* pEnt, const AABB & objBox, const float fObjRadiusSqr);
	void CheckVis(int nEngineFrameID);
	void DrawVisibleSectors(int nEngineFrameID);
	void ActivatePortal(const Vec3 &vPos, bool bActivate, const char * szEntityName);
	void UpdateVisArea(CVisArea * pArea, const Vec3 * pPoints, int nCount, const char * szName, const SVisAreaInfo & info);
	VIRTUAL void UpdateConnections();
	void MoveObjectsIntoList(PodArray<SRNInfo> * plstVisAreasEntities, const AABB & boxArea, bool bRemoveObjects = false);
	IVisArea * GetVisAreaFromPos(const Vec3 &vPos);
	bool IntersectsVisAreas(const AABB& box, void** pNodeCache = 0);
	bool ClipOutsideVisAreas(Sphere& sphere, Vec3 const& vNormal, void* pNodeCache = 0);
	bool IsEntityVisAreaVisible(IRenderNode * pEnt, int nMaxReqursion, const CDLight * pLight, int nEngineFrameID);
	void MakeActiveEntransePortalsList(const CCamera * pCamera, PodArray<CVisArea *> & lstActiveEntransePortals, CVisArea * pThisPortal);
  void MergeCameras(CCamera & cam, const CCamera & camPlus);
  void DrawOcclusionAreasIntoCBuffer(CCullBuffer * pCBuffer);
  bool IsValidVisAreaPointer(CVisArea * pVisArea);
  void GetStreamingStatus(int & nLoadedSectors, int & nTotalSectors);
  void GetMemoryUsage(ICrySizer*pSizer) const;
	bool IsOccludedByOcclVolumes(const AABB& objBox, bool bCheckOnlyIndoorVolumes = false);
	void GetObjectsAround(Vec3 vExploPos, float fExploRadius, PodArray<SRNInfo> * pEntList, bool bSkip_ERF_NO_DECALNODE_DECALS = false, bool bSkipDynamicObjects = false);
	void DoVoxelShape(Vec3 vWSPos, float fRadius, int nSurfaceTypeId, Vec3 vBaseColor, EVoxelEditOperation eOperation, EVoxelBrushShape eShape, EVoxelEditTarget eTarget, PodArray<CVoxelObject*> * pAffectedVoxAreas);
	void IntersectWithBox(const AABB & aabbBox, PodArray<CVisArea*> * plstResult, bool bOnlyIfVisible);
  template <class T>
  bool Load_T(T * & f, int & nDataSize, struct SVisAreaManChunkHeader * pVisAreaManagerChunkHeader, std::vector<struct IStatObj*> * pStatObjTable, std::vector<IMaterial*> * pMatTable, bool bHotUpdate, SHotUpdateInfo * pExportInfo);
  virtual bool Load(FILE * & f, int & nDataSize, struct SVisAreaManChunkHeader * pVisAreaManagerChunkHeader, std::vector<struct IStatObj*> * pStatObjTable, std::vector<IMaterial*> * pMatTable);
  VIRTUAL bool SetCompiledData(byte * pData, int nDataSize, std::vector<struct IStatObj*> ** ppStatObjTable, std::vector<IMaterial*> ** ppMatTable, bool bHotUpdate, SHotUpdateInfo * pExportInfo, const Vec3 &vSegmentOrigin);
	VIRTUAL bool GetCompiledData(byte * pData, int nDataSize, std::vector<struct IStatObj*> ** ppStatObjTable, std::vector<IMaterial*> ** ppMatTable, std::vector<struct IStatInstGroup*> ** ppStatInstGroupTable, EEndian eEndian, SHotUpdateInfo * pExportInfo, const Vec3 &segmentOffset);
	VIRTUAL int GetCompiledDataSize(SHotUpdateInfo * pExportInfo);
  void UnregisterEngineObjectsInArea(const SHotUpdateInfo * pExportInfo, PodArray<IRenderNode *> & arrUnregisteredObjects, bool bOnlyEngineObjects);
	void PrecacheLevel(bool bPrecacheAllVisAreas, Vec3 * pPrecachePoints, int nPrecachePointsNum);
	void AddLightSource(CDLight * pLight, int nEngineFrameID);
	void AddLightSourceReqursive(CDLight * pLight, CVisArea* pArea, const int32 nDeepness, int nEngineFrameID);
	bool IsEntityVisAreaVisibleReqursive(CVisArea * pVisArea, int nMaxReqursion, PodArray<CVisArea*> * pUnavailableAreas, const CDLight * pLight, int nEngineFrameID);
  bool IsAABBVisibleFromPoint(AABB & aabb, Vec3 vPos);
  bool FindShortestPathToVisArea(CVisArea * pThisArea, CVisArea * pTargetArea, PodArray<CVisArea*> & arrVisitedAreas, int & nRecursion, const struct Shadowvolume & sv);

	int				GetNumberOfVisArea() const;																				// the function give back the accumlated number of visareas and portals
	IVisArea *GetVisAreaById( int nID ) const;																	// give back the visarea interface based on the id (0..GetNumberOfVisArea()) it can be a visarea or a portal

	VIRTUAL void AddListener( IVisAreaCallback *pListener );
	VIRTUAL void RemoveListener( IVisAreaCallback *pListener );
	
	void MarkAllSectorsAsUncompiled();

	// -------------------------------------

	void GetObjectsByType( PodArray<IRenderNode*> & lstObjects, EERType objType, const AABB * pBBox, float fViewDist = -1);
	void GetObjects( PodArray<IRenderNode*> & lstObjects, const AABB * pBBox, float fViewDist = -1);
  CVisArea * GetCurVisArea() { return m_pCurArea ? m_pCurArea : m_pCurPortal; }
  void GenerateStatObjAndMatTables(std::vector<IStatObj*> * pStatObjTable, std::vector<IMaterial*> * pMatTable, std::vector<IStatInstGroup*> * pStatInstGroupTable, SHotUpdateInfo * pExportInfo);
  void OnVisAreaDeleted(IVisArea * pArea);
  void ActivateObjectsLayer(uint16 nLayerId, bool bActivate, bool bPhys);

  VIRTUAL void PrepareSegmentData(const AABB &box);
  VIRTUAL void ReleaseInactiveSegments();
  VIRTUAL bool CreateSegment(int nSID);
  VIRTUAL bool DeleteSegment(int nSID, bool bDeleteNow);
  VIRTUAL bool StreamCompiledData(uint8 * pData, int nDataSize, int nSID, std::vector<struct IStatObj*> * pStatObjTable, std::vector<IMaterial*> * pMatTable, std::vector<struct IStatInstGroup*> * pStatInstGroupTable, const Vec3 &vSegmentOrigin, const Vec2 &vIndexOffset);
  VIRTUAL void OffsetPosition(const Vec3& delta);

private:
	void DeleteAllVisAreas();

	CVisArea * CreateTypeVisArea();
	CVisArea * CreateTypePortal();
	CVisArea * CreateTypeOcclArea();

	void DeleteVisAreaSegment(int nSID, PodArray<CVisAreaSegmentData> &visAreaSegmentData, PodArray<CVisArea *> &lstVisAreas, PodArray<CVisArea *, ReservedVisAreaBytes> &visAreas, PodArray<int> &deletedVisAreas);
	CVisArea * FindVisAreaByGuid(VisAreaGUID guid, PodArray<CVisArea *> &lstVisAreas);
	CSWVisArea * FindFreeVisAreaFromPool(PodArray<CVisArea *, ReservedVisAreaBytes> &visAreas);
	template<class T>
	CSWVisArea * CreateVisAreaFromPool(PodArray<CVisArea *> &lstVisAreas, PodArray<CVisArea *, ReservedVisAreaBytes> &visAreas, PodArray<T> &visAreaColdData, bool bIsPortal);
	template<class T>
	void ResetVisAreaList(PodArray<CVisArea *> &lstVisAreas, PodArray<CVisArea *, ReservedVisAreaBytes> &visAreas, PodArray<T> &visAreaColdData);
	template<class T>
	CSWVisArea * CreateTypeArea(PodArray<CVisArea *, ReservedVisAreaBytes> &visAreas, PodArray<T> &visAreaColdData, bool bIsPortal);

	PodArray<CVisArea *, ReservedVisAreaBytes> m_portals;
	PodArray<CVisArea *, ReservedVisAreaBytes> m_visAreas;
	PodArray<CVisArea *, ReservedVisAreaBytes> m_occlAreas;

	PodArray<SGenericColdData>	m_visAreaColdData;
	PodArray<SPortalColdData>		m_portalColdData;
	PodArray<SGenericColdData>	m_occlAreaColdData;	

	PodArray<CVisAreaSegmentData> m_visAreaSegmentData;
	PodArray<CVisAreaSegmentData> m_portalSegmentData;
	PodArray<CVisAreaSegmentData> m_occlAreaSegmentData;

	PodArray<int> m_arrDeletedVisArea;
	PodArray<int> m_arrDeletedPortal;
	PodArray<int> m_arrDeletedOcclArea;

	struct SActiveVerts
	{
		Vec3 arrvActiveVerts[4];
	};

#if defined(OCCLUSIONCULLER_W)
	std::vector<SActiveVerts> m_allActiveVerts;
#endif
};

#endif // VisArea_H
